﻿// Decompiled with JetBrains decompiler
// Type: Netick.NetworkConnection
// Assembly: Netick, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
// MVID: 6726ECA1-C773-4CF7-8952-E81B30D93B52
// Assembly location: D:\WorkSpaceDemo\UPM\NetickForUnity\Netick\Runtime\Netick.dll
// XML documentation location: D:\WorkSpaceDemo\UPM\NetickForUnity\Netick\Runtime\Netick.xml

using System;
using System.Collections.Generic;

#nullable disable
namespace Netick;

public abstract class NetworkConnection : NetworkPlayer
{
  public unsafe int* RemoteUserData;
  public unsafe int* LocalUserData;
  public int MaxOutgoingDataPerTickInBytes;
  internal bool IsReady;
  internal Tick ConnectionTick = Tick.InvalidTick;
  internal Tick LastReceivedSnapshotTick = Tick.InvalidTick;
  internal Tick LastReceivedFullSnapshotTick = Tick.InvalidTick;
  internal Tick LastReceivedRemoteSnapshotTickAck = Tick.InvalidTick;
  internal Tick DCTick = Tick.InvalidTick;
  internal Tick LastReceivedFullSnapshotTick_State = Tick.InvalidTick;
  internal Tick BasisSnapshotAck = Tick.InvalidTick;
  internal Tick LastSnapshotTick = Tick.InvalidTick;
  internal Tick FirstSentSnapshotTick = Tick.InvalidTick;
  internal uint AckBitfield;
  internal NetAck Ack = NetAck.Invalid;
  private int _receivedDataAccumulator;
  private int _expectedFragments = -1;
  private List<int> _receivedFragments = new List<int>(128 /*0x80*/);
  internal List<BufferedRpc> BufferedRpcs = new List<BufferedRpc>(128 /*0x80*/);
  internal UnreliableRpcChannel _unreliableRpcChannel;
  internal ReliableRpcChannel _reliableRpcChannel;
  internal BitBuffer _readBuffer;
  internal BitBuffer _writeBuffer;
  internal LossCalculator InLossCalculator;
  internal DoubleStats PacketReceiveDelta;
  internal double[] _sentUpdatesTimes;
  private double _lastPacketTime;
  public readonly DoubleStats RTT;
  internal const int _fragHeaderWords = 4;
  internal const int _fragHeaderBits = 128 /*0x80*/;
  private const int _customPacketHeaderSizeBytes = 5;

  public TransportConnection TransportConnection { get; internal set; }

  public IEndPoint EndPoint => this.TransportConnection.EndPoint;

  public int Id => !this.Engine.IsClient ? this.Index : 0;

  public bool ReplicateEntities { get; set; } = true;

  public DataAccumulator BytesIn { get; private set; }

  public DataAccumulator BytesOut { get; private set; }

  /// <summary>
  /// The longest inward packet loss burst (more than 2 packets lost in a row) in the last 10 seconds.
  /// </summary>
  public int InPacketLossBurst => this.InLossCalculator.LossBurst;

  /// <summary>
  /// Estimation of incoming packets that are being lost. Value ranges between 0.0 and 1.0, with 1.0 meaning all packets are being lost.
  /// </summary>
  public float InPacketLoss => this.InLossCalculator.LossRate;

  /// <summary>
  /// Estimation of outgoing packets that are being lost. Value ranges between 0.0 and 1.0, with 1.0 meaning all packets are being lost.
  /// </summary>
  public float OutPacketLoss { get; private set; }

  internal abstract void ReadPayload(
    BitBuffer buffer,
    Tick snapshotTick,
    Tick ackSnaphotTick,
    Tick lastSnapshotTick);

  internal unsafe NetworkConnection(short index, NetickEngine engine)
    : base(engine)
  {
    this.Index = (int) index;
    this.MaxOutgoingDataPerTickInBytes = engine._config.MaxDataPerConnectionPerTickInBytes;
    this.RemoteUserData = (int*) MemoryAllocation.Malloc((long) (engine._gameEngine.GetConnectionMetaSizeWords() * 4));
    this.LocalUserData = (int*) MemoryAllocation.Malloc((long) (engine._gameEngine.GetConnectionMetaSizeWords() * 4));
    int wordSize = NetickUtils.GetWordSize(this.MaxOutgoingDataPerTickInBytes);
    this._readBuffer = new BitBuffer(wordSize + 50);
    this._writeBuffer = new BitBuffer(wordSize + 50);
    this._unreliableRpcChannel = new UnreliableRpcChannel(engine, this);
    this._reliableRpcChannel = new ReliableRpcChannel(engine, this);
    this._sentUpdatesTimes = new double[engine._config.MaxSnapshots];
    this.PacketReceiveDelta = new DoubleStats((int) ((double) engine._config.MaxSnapshots * 1.5));
    this.RTT = new DoubleStats((int) ((double) engine._config.MaxSnapshots * 1.0));
    this.BytesIn = new DataAccumulator(2);
    this.BytesOut = new DataAccumulator(2);
    float num = !engine.IsServer ? engine._config.TickRate / (float) engine._config.ServerDivisor : engine._config.TickRate;
    this.InLossCalculator = new LossCalculator(num * 3f, num * 5f);
  }

  unsafe ~NetworkConnection()
  {
    MemoryAllocation.Free((void*) this.RemoteUserData);
    MemoryAllocation.Free((void*) this.LocalUserData);
  }

  internal override unsafe void Reset()
  {
    base.Reset();
    this.TransportConnection = (TransportConnection) null;
    this.IsReady = false;
    this.ConnectionTick = Tick.InvalidTick;
    this.LastReceivedSnapshotTick = Tick.InvalidTick;
    this.LastReceivedRemoteSnapshotTickAck = Tick.InvalidTick;
    this.DCTick = Tick.InvalidTick;
    this.LastReceivedFullSnapshotTick_State = Tick.InvalidTick;
    this._lastPacketTime = 0.0;
    this._receivedDataAccumulator = 0;
    this._expectedFragments = -1;
    this._receivedFragments.Clear();
    this._unreliableRpcChannel.Reset();
    this._reliableRpcChannel.Reset();
    this.InLossCalculator.Reset();
    this.PacketReceiveDelta.ResetTo((double) this.Engine.Config.ServerDivisor * (double) this.Engine.FixedDeltaTime);
    this.RTT.ResetTo((double) Math.Max(this.Engine.FixedDeltaTime * (float) this.Engine.Config.ServerDivisor, 0.05f));
    this.BytesIn.Reset();
    this.BytesOut.Reset();
    for (int index = 0; index < this.BufferedRpcs.Count; ++index)
      this.Engine._rpcManager.RpcAllocator.Free((void*) this.BufferedRpcs[index].Data, this.BufferedRpcs[index].SizeWords * 4);
    this.BufferedRpcs.Clear();
  }

  internal virtual void OnConnected(TransportConnection peer)
  {
    this.Reset();
    this.TransportConnection = peer;
    this.IsReady = true;
    this.ConnectionTick = this.Engine.AuthoritativeTick;
  }

  internal virtual void OnDisconnected(
    TransportDisconnectReason transportDisconnectReason,
    bool releaseControlledEntities)
  {
    this.IsReady = false;
  }

  internal void Update(float deltaTime)
  {
    this.BytesIn.Update(deltaTime);
    this.BytesOut.Update(deltaTime);
  }

  internal virtual void EnqueueRpc(NetworkRpc rpc)
  {
    if (rpc.IsReliable)
      this._reliableRpcChannel.EnqueueMsg(rpc);
    else
      this._unreliableRpcChannel.EnqueueMsg(rpc);
  }

  internal unsafe void SendPackets(bool send)
  {
    BitBuffer writeBuffer = this._writeBuffer;
    if (this.TransportConnection == null)
      return;
    int wordSize1 = NetickUtils.GetWordSize(writeBuffer.Length());
    int wordSize2 = NetickUtils.GetWordSize(this.TransportConnection.Mtu - 100);
    int num1 = wordSize2 * 4;
    int* chunks = (int*) writeBuffer._chunks;
    int num2 = wordSize1;
    int num3 = 0;
    int num4 = this.Engine._snapshotTick.Value;
    int num5 = this.LastReceivedFullSnapshotTick.Value;
    while (num2 > 0)
    {
      bool flag = num2 - wordSize2 <= 0;
      IntPtr ptr = new IntPtr((void*) (chunks - 4));
      *(chunks - 4) = wordSize2 << 8;
      (chunks - 4)[1] = (flag ? num3 + 1 : -(num3 + 1)) << 16 /*0x10*/;
      (chunks - 4)[2] = num4;
      (chunks - 4)[3] = num5;
      if (send)
      {
        int num6;
        if (flag)
        {
          num6 = (num2 + 1) * 4 + 16 /*0x10*/;
          this.TransportConnection.Send(ptr, (num2 + 1) * 4 + 16 /*0x10*/);
        }
        else
        {
          num6 = num1 + 16 /*0x10*/;
          this.TransportConnection.Send(ptr, num6);
        }
        this.BytesOut.Add(num6);
      }
      if (!flag)
        chunks += wordSize2;
      num2 -= wordSize2;
      ++num3;
    }
    if (this.FirstSentSnapshotTick.IsValid)
      return;
    this.FirstSentSnapshotTick = this.Engine._snapshotTick;
  }

  internal void WritePackets(SerializationCache cache)
  {
    this.WritePacketsFirstPass();
    this.WritePacketsSecondPass(cache);
  }

  internal unsafe void WritePacketsFirstPass()
  {
    this._writeBuffer._chunks = this._writeBuffer._chunksOriginal + 4;
    this._writeBuffer.Clear();
    this._writeBuffer.AddByte((byte) Math.Round((double) this.InLossCalculator.LossRate * 100.0));
    for (int index = 0; index < this.Engine._connectionUserHeaderSizeWords; ++index)
      this._writeBuffer.AddInt4(this.LocalUserData[index]);
    this._reliableRpcChannel.Write(this._writeBuffer);
    this._unreliableRpcChannel.Write(this._writeBuffer);
  }

  internal virtual int WritePacketsSecondPass(SerializationCache cache) => 0;

  /// <summary>
  /// Sends data to this connection.
  /// <para>Note: the low-level transport must implement <see cref="M:Netick.TransportConnection.SendUserData(System.IntPtr,System.Int32,Netick.TransportDeliveryMethod)" /> for this method to have an effect.</para>
  /// </summary>
  public unsafe void SendData(
    byte id,
    byte* data,
    int length,
    TransportDeliveryMethod deliveryMethod)
  {
    if (!this.IsReady)
      return;
    ReadOnlySpan<byte> readOnlySpan = new ReadOnlySpan<byte>((void*) data, length);
    byte* numPtr = stackalloc byte[length + 5];
    Span<byte> span1 = new Span<byte>((void*) numPtr, length + 5);
    Span<byte> span2 = span1.Slice(5);
    *(int*) numPtr = 0;
    span1[4] = id;
    readOnlySpan.CopyTo(span2);
    this.TransportConnection.SendUserData(new IntPtr((void*) numPtr), length + 5, deliveryMethod);
    this.BytesOut.Add(length + 5);
  }

  public unsafe void SendData(
    byte id,
    byte[] data,
    int length,
    TransportDeliveryMethod deliveryMethod)
  {
    fixed (byte* data1 = data)
      this.SendData(id, data1, length, deliveryMethod);
  }

  internal unsafe void ReadPacket(BitBuffer reader, TransportDeliveryMethod transportDeliveryMethod)
  {
    if (!this.IsReady)
      return;
    int writtenByteSize = reader.writtenByteSize;
    if (writtenByteSize < 4)
      return;
    this.BytesIn.Add(writtenByteSize);
    int* chunks = (int*) reader._chunks;
    int val1 = (int) ((long) *chunks & 4294967040L) >> 8;
    if (val1 == 0)
    {
      if (writtenByteSize < 5)
        return;
      byte* numPtr = (byte*) chunks;
      int len = writtenByteSize - 5;
      this.Engine._gameEngine.OnReceiveData(this, numPtr[4], numPtr + 5, len, transportDeliveryMethod);
    }
    else
    {
      int num1 = chunks[1] >> 16 /*0x10*/;
      int num2;
      int num3;
      if (num1 > 0)
      {
        num2 = num1 - 1;
        num3 = 1;
      }
      else
      {
        num2 = -num1 - 1;
        num3 = 0;
      }
      Tick tick = new Tick(chunks[2]);
      Tick ackSnaphotTick = new Tick(chunks[3]);
      reader.ReadPosition = 128 /*0x80*/;
      if (tick < this.LastReceivedSnapshotTick && this.LastReceivedSnapshotTick.IsValid)
        return;
      if (num3 == 1)
        this._expectedFragments = num2 + 1;
      int num4 = tick - this.LastReceivedSnapshotTick;
      Tick receivedSnapshotTick = this.LastReceivedSnapshotTick;
      this.LastReceivedSnapshotTick = tick;
      if (!receivedSnapshotTick.IsValid || num4 == 0)
      {
        if (this._receivedFragments.Contains(num2))
          return;
        this._receivedDataAccumulator += reader.Length();
        this._receivedFragments.Add(num2);
      }
      else
      {
        this._readBuffer.Clear();
        this._receivedFragments.Clear();
        this._receivedDataAccumulator = 0;
        this._receivedDataAccumulator += reader.Length();
        this._receivedFragments.Add(num2);
        if (num3 != 1)
          this._expectedFragments = -1;
      }
      int num5 = Math.Min(val1, 2000);
      int num6 = num2 * num5;
      int num7 = NetickUtils.GetWordSize(reader.writtenByteSize) - 4;
      MemoryAllocation.Copy((void*) (this._readBuffer._chunks + num6), (void*) (reader._chunks + 4), (long) (num7 * 4));
      if (this._receivedFragments.Count != this._expectedFragments)
        return;
      this._receivedFragments.Clear();
      this.LastReceivedFullSnapshotTick = tick;
      if (ackSnaphotTick.IsValid)
        this.LastReceivedRemoteSnapshotTickAck = ackSnaphotTick;
      this._readBuffer.ReadPosition = 0;
      this.OutPacketLoss = (float) this._readBuffer.ReadByte() / 100f;
      for (int index = 0; index < this.Engine._connectionUserHeaderSizeWords; ++index)
        this.RemoteUserData[index] = this._readBuffer.ReadInt4();
      this._reliableRpcChannel.Read(this._readBuffer);
      this._unreliableRpcChannel.Read(this._readBuffer);
      if (this.Engine.IsServer)
        NetworkRpc.InvokeRpcs(this.Engine, this);
      if (this.Engine.IsClient)
        this.Engine._profiler.SnapshotSize.Update((float) this._receivedDataAccumulator);
      this.InLossCalculator.OnTickReceive(tick);
      double time = this.Engine._timer.Time;
      double num8 = time - this._lastPacketTime;
      if (this._lastPacketTime != 0.0 && ((double) (this.Engine.AuthoritativeTick - this.ConnectionTick) > (double) this.Engine._config.TickRate && this.ReplicateEntities || this.Engine.IsClient))
        this.PacketReceiveDelta.Update(num8);
      if (ackSnaphotTick.IsValid)
      {
        double v = Math.Min((double) (this.Engine._snapshotTick - ackSnaphotTick) * (double) this.Engine.FixedDeltaTime + (double) this.Engine.FixedDeltaTime, this.Engine._timer.Time - this._sentUpdatesTimes[ackSnaphotTick % this.Engine._config.MaxSnapshots]);
        if (this._lastPacketTime != 0.0)
          this.RTT.Update(v);
        if (this._lastPacketTime != 0.0)
          this.Engine._profiler.RTT.Update((float) this.RTT.Latest);
        if (this.Engine.IsClient && this.Engine.SnapshotTick - this.Engine.Client.ConnectedServer.ConnectionTick < 20)
        {
          this.RTT.ResetTo(v);
          this.Engine._profiler.RTT.ResetTo((float) this.RTT.Latest);
        }
      }
      this._lastPacketTime = time;
      if (tick > this.LastSnapshotTick || !this.LastSnapshotTick.IsValid)
      {
        int num9 = tick - this.LastSnapshotTick;
        if (!this.LastSnapshotTick.IsValid)
          num9 = 0;
        if (num9 >= 30)
        {
          this.AckBitfield = 0U;
          num9 = 0;
        }
        this.AckBitfield = (uint) ((int) this.AckBitfield << num9 | 1);
      }
      this.LastSnapshotTick = tick;
      this.ReadPayload(this._readBuffer, tick, ackSnaphotTick, receivedSnapshotTick);
      this.Engine._gameEngine.OnPacketReceived(this);
    }
  }
}
